/* Copyright (c) 2025 Beijing Semidrive Technology Corporation
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "arm_mpu.h"
#include "arch_irq.h"

#define PSR_F_BIT           (1 << 6)
#define PSR_I_BIT           (1 << 7)

#define MODE_USR            0x10
#define MODE_FIQ            0x11
#define MODE_IRQ            0x12
#define MODE_SVC            0x13
#define MODE_ABT            0x17
#define MODE_UND            0x1B
#define MODE_SYS            0x1F

#define MODE_MASK           0x1F

static inline uint32 read_cpsr(void)
{
    uint32 cpsr;
    ASM volatile("mrs   %0, cpsr" : "=r" (cpsr));
    return cpsr;
}

/*
 * is irq masked.
 *
 * @return   masked or not.
 */
boolean arch_irq_is_masked(void)
{
    uint32 cpsr = read_cpsr();
    return (!!(cpsr & PSR_I_BIT));
}

/*
 * is irq mode.
 *
 * @return   irq mode or not.
 */
boolean arch_in_irq_mode(void)
{
    uint32 cpsr;
    uint8 mode;

    cpsr = read_cpsr();
    mode = cpsr & MODE_MASK;
    return ((mode != MODE_USR) && (mode != MODE_SYS));
}

/**
 * @brief Enable vectored interrupt mode.
 */
void arch_vectored_irq_enable(boolean en)
{
    uint32 sctlr;

    sctlr = arm_read_sctlr();

    if (en) {
        sctlr |= 1U << 24;
    }
    else {
        sctlr &= ~(1U << 24);
    }

    arm_write_sctlr(sctlr);
}

/**
 * @brief Whether core is in FIQ mode or not
 *
 * @return true FIQ mode
 * @return false otherwise
 */
boolean arch_in_fiq_mode(void)
{
    uint32 cpsr = read_cpsr();

    return (cpsr & MODE_MASK) == MODE_FIQ;
}

/*
 * cpsr irq si masked.
 *
 * @return  masked or not.
 */
boolean prev_irq_is_masked(uint32 cpsr)
{
    return (!!(cpsr & PSR_I_BIT));
}

/*
 * irq enable.
 */
void arch_irq_enable(void)
 {
     COMPILER_BARRIER();
 #ifndef CFG_USER_MODE_CASE
     ASM volatile("cpsie i");
 #else
     ARCH_IRQ_OPERATE("\tsvc #5\n");
 #endif /* #ifndef CFG_USER_MODE_CASE */
 }

 /*
  * irq disable.
  */
void arch_irq_disable(void)
 {
 #ifndef CFG_USER_MODE_CASE
     ASM volatile("cpsid i");
 #else
     ARCH_IRQ_OPERATE("\tsvc #3\n");
 #endif /* #ifndef CFG_USER_MODE_CASE */
     COMPILER_BARRIER();
 }

 /*
  * irq save.
  *
  * @return  old irq state.
  */
irq_state_t arch_irq_save(void)
 {
     unsigned int cpsr;
 #ifndef CFG_USER_MODE_CASE
     ASM volatile
     (
         "\tmrs    %0, cpsr\n"
         "\tcpsid  i\n"
         : "=r" (cpsr)
         :
         : "memory"
     );
 #else
     ARCH_IRQ_SAVE();
 #endif

     return cpsr;
 }

 /*
  * irq restore.
  *
  * @flags   old irq state.
  */
void arch_irq_restore(irq_state_t flags)
 {
#ifndef CFG_USER_MODE_CASE
     ASM volatile
     (
         "msr    cpsr_c, %0"
         :
         : "r" (flags)
         : "memory"
     );
#else
    ARCH_IRQ_OPERATE("\tsvc #8\n");
#endif
 }